using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;

using KmlUtils;

namespace StormModelToKMLDB
{
    public class TempBitmap
    {
        public enum ColorType
        {
            Smooth,
            Contoured,
            TwentySixPlus
        }
        #region Private Members
        private string _fileName;
        private int _lat, _lon, _span;
        private DateTime _dateTime;
        private byte[] _bytes;
        #endregion

        #region Constructors
        /// <summary>
        /// Create a TempMap that stores a temperature at each grid location
        /// </summary>
        /// <param name="rawData">the entire raw data set</param>
        /// <param name="lat">the lat of the LL corner for which you want to decode</param>
        /// <param name="lon">the lon of the LL corner for which you want to decode</param>
        /// <param name="degSpan">the span of the map in degrees</param>
        /// <param name="fileName">the filename which corresponds to the relative URI that the GRIB data is stored</param>
        public TempBitmap(byte[] rawData, int lat, int lon, int degSpan, string fileName)
        {
            this._bytes = rawData;
            this._lat = lat;
            this._lon = lon;
            this._span = degSpan;
            this._fileName = fileName;
            this._dateTime = DecodeFileName(this._fileName);
        }
        #endregion

        #region Public Methods / Properties
        public LatLonBox LatLonBox
        {
            get
            {
                LatLonBox llb = new LatLonBox(this.Lat + this.SpanDeg, this.Lat, this.Lon + this.SpanDeg, this.Lon, 0);
                return llb;
            }
        }
        public static bool Update(TempBitmap oldTBM, TempBitmap newTBM)
        {
            if ((oldTBM.Lat != newTBM.Lat) || (oldTBM.Lon != newTBM.Lon) || (oldTBM.SpanDeg != newTBM.SpanDeg))
                return false;

            oldTBM.Bytes = newTBM.Bytes;
            oldTBM.DateTime = newTBM.DateTime;
            oldTBM.Filename = newTBM.Filename;
            return true;
        }

        public byte[] Bytes
        {
            get { return this._bytes; }
            set { this._bytes = value; }
        }

        public int Lat
        {
            get { return this._lat; }
        }

        public int Lon
        {
            get { return this._lon; }
        }

        public int SpanDeg
        {
            get { return this._span; }
        }

        public string Filename
        {
            get { return this._fileName; }
            set { this._fileName = value; }
        }

        public DateTime DateTime
        {
            get { return this._dateTime; }
            set
            {
                this._dateTime = value;
            }
        }
        #endregion

        #region Make Bitmaps and Colors

        public Bitmap MakeSSTMap()
        {
            return this.MakeSSTMap(ColorType.Smooth);
        }

        public Bitmap MakeContourSSTMap()
        {
            return this.MakeSSTMap(ColorType.Contoured);
        }

        public Bitmap Make26SSTMap()
        {
            return this.MakeSSTMap(ColorType.TwentySixPlus);
        }

        public Bitmap MakeSSTMap(ColorType type)
        {
            // Initialize Bitmap
            Bitmap bm = new Bitmap(this._span * 4, this._span * 4, PixelFormat.Format32bppArgb);

            //Convert +/- Lat to 0->180 in Bytes
            int nsByteStart = 0;
            if (this._lat < 0) // S
            {
                nsByteStart = 1440 * (90 + this._lat) * 4;
            }
            else if (this._lat == 0) // Equator
            {
                nsByteStart = 1440 * 90 * 4;
            }
            else if (this._lat > 0) // N
            {
                nsByteStart = 1440 * (90 + this._lat) * 4;
            }

            //Convert =/- Lon to 0->360 in Bytes
            int ewByteStart = 0;
            if (this._lon < 0)
            {
                ewByteStart = 720;
                ewByteStart += (180 + this._lon) * 4;
            }
            else if (this._lon == 0)
            {
                ewByteStart = 0;
            }
            else if (this._lon > 0)
            {
                ewByteStart += this._lon * 4;
            }

            // Starting point in byte array
            int startByte = nsByteStart + ewByteStart;

            // Bitmap byte width (Square)
            int byteWidth = this._span * 4;

            // Paint the Bitmap Byte by Byte
            int row = 0;
            try
            {
                for (int i = 0; i < byteWidth; i++)// Rows from bottom
                {
                    for (int j = 0; j < byteWidth; j++)// Column from left
                    {
                        double deg1 = ((.15 * this._bytes[(startByte + (1440 * row)) + j]) - 3.0);
                        Color c = new Color();
                        if (this._bytes[(startByte + (1440 * row)) + j] > 250)
                            c = Color.Transparent;
                        else
                        {
                            switch (type)
                            {
                                case ColorType.Smooth:
                                    c = this.MakeSmoothColor(deg1);
                                    break;
                                case ColorType.Contoured:
                                    c = this.MakeContourColor(deg1);
                                    break;
                                case ColorType.TwentySixPlus:
                                    c = this.MakeColor26(deg1);
                                    break;
                            }
                        }
                        bm.SetPixel(j, byteWidth - i - 1, c);
                    }
                    row++;
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }

            return bm;
        }

        public static Bitmap MakeDeltaSSTMap(TempBitmap minuend, TempBitmap subtrahend)
        {
            // Initialize Bitmap
            Bitmap bm = new Bitmap(minuend.SpanDeg * 4, minuend.SpanDeg * 4, PixelFormat.Format32bppArgb);

            //Convert +/- Lat to 0->180 in Bytes
            int nsByteStart = 0;
            if (minuend.Lat < 0) // S
            {
                nsByteStart = 1440 * (90 + minuend.Lat) * 4;
            }
            else if (minuend.Lat == 0) // Equator
            {
                nsByteStart = 1440 * 90 * 4;
            }
            else if (minuend.Lat > 0) // N
            {
                nsByteStart = 1440 * (90 + minuend.Lat) * 4;
            }

            //Convert =/- Lon to 0->360 in Bytes
            int ewByteStart = 0;
            if (minuend.Lon < 0)
            {
                ewByteStart = 720;
                ewByteStart += (180 + minuend.Lon) * 4;
            }
            else if (minuend.Lon == 0)
            {
                ewByteStart = 0;
            }
            else if (minuend.Lon > 0)
            {
                ewByteStart += minuend.Lon * 4;
            }

            // Starting point in byte array
            int startByte = nsByteStart + ewByteStart;

            // Bitmap byte width (Square)
            int byteWidth = minuend.SpanDeg * 4;

            // Paint the Bitmap Byte by Byte
            int row = 0;
            try
            {
                for (int i = 0; i < byteWidth; i++)// Rows from bottom
                {
                    for (int j = 0; j < byteWidth; j++)// Column from left
                    {
                        double deg1 = ((.15 * minuend.Bytes[(startByte + (1440 * row)) + j]) - 3.0);
                        double deg2 = ((.15 * subtrahend.Bytes[(startByte + (1440 * row)) + j]) - 3.0);
                        Color c = new Color();
                        if (minuend.Bytes[(startByte + (1440 * row)) + j] > 250)
                            c = Color.Transparent;
                        else
                        {
                            c = TempBitmap.MakeColorDelta(deg1 - deg2);
                        }
                        bm.SetPixel(j, byteWidth - i - 1, c);
                    }
                    row++;
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }

            return bm;
        }

        private Color MakeSmoothColor(double d)
        {
            Color c = new Color();

            int r, g, b = 0;

            if (d >= 0 && d < 5)
            {
                r = 0;
                g = 0;
                b = 0;
                b += ((int)((d / 5.0) * 255.0));
                c = Color.FromArgb(r, g, b);
            }
            if (d >= 5 && d < 10)
            {
                r = 0;
                g = 0;
                b = 255;
                g += ((int)(((d - 5.0) / 5.0) * 255.0));
                c = Color.FromArgb(r, g, b);
            }
            if (d >= 10 && d < 15)
            {
                r = 0;
                g = 255;
                b = 255;
                b -= ((int)(((d - 10.0) / 5.0) * 255.0));
                c = Color.FromArgb(r, g, b);
            }
            if (d >= 15 && d < 20)
            {
                r = 0;
                g = 255;
                b = 0;
                r += ((int)(((d - 15.0) / 5.0) * 255.0));
                c = Color.FromArgb(r, g, b);
            }
            if (d >= 20 && d < 25)
            {
                r = 255;
                g = 255;
                b = 0;
                //g += ((int)(((d - 28.0) / 6.5) * 255.0));
                g -= ((int)(((d - 20.0) / 5.0) * 255.0));
                c = Color.FromArgb(r, g, b);

            }
            if (d >= 25 && d < 30)
            {
                r = 255;
                g = 0;
                b = 0;
                b += ((int)(((d - 25.0) / 5.0) * 255.0));
                c = Color.FromArgb(r, g, b);
            }
            if (d >= 30 && d < 34.5)
            {
                r = 255;
                g = 0;
                b = 255;
                g += ((int)(((d - 30.0) / 4.5) * 255.0));
                c = Color.FromArgb(r, g, b);
            }
            if (d >= 34.5)
                c = Color.Transparent;


            return c;
        }

        private Color MakeContourColor(double d)
        {
            Color c = new Color();

            int r, g, b = 0;

            //===================================================================
            if (d >= 0 && d < 5)
            {
                r = 0;
                g = 0;
                b = 0;
                b += ((int)((d / 5.0) * 255.0));
                if (b < 85)
                    b = 42;
                if (b >= 85 && b < 170)
                    b = 127;
                if (b >= 170)
                    b = 212;
                c = Color.FromArgb(r, g, b);
            }
            if (d >= 5 && d < 10)
            {
                r = 0;
                g = 0;
                b = 255;
                g += ((int)(((d - 5.0) / 5.0) * 255.0));
                if (g < 85)
                    g = 42;
                if (g >= 85 && g < 170)
                    g = 127;
                if (g >= 170)
                    g = 212;
                c = Color.FromArgb(r, g, b);
            }
            if (d >= 10 && d < 15)
            {
                r = 0;
                g = 255;
                b = 255;
                b -= ((int)(((d - 10.0) / 5.0) * 255.0));
                if (b < 85)
                    b = 42;
                if (b >= 85 && b < 170)
                    b = 127;
                if (b >= 170)
                    b = 212;
                c = Color.FromArgb(r, g, b);
            }
            if (d >= 15 && d < 20)
            {
                r = 0;
                g = 255;
                b = 0;
                r += ((int)(((d - 15.0) / 5.0) * 255.0));
                if (r < 85)
                    r = 42;
                if (r >= 85 && r < 170)
                    r = 127;
                if (r >= 170)
                    r = 212;
                c = Color.FromArgb(r, g, b);
            }
            if (d >= 20 && d < 25)
            {
                r = 255;
                g = 255;
                b = 0;
                //g += ((int)(((d - 28.0) / 6.5) * 255.0));
                g -= ((int)(((d - 20.0) / 5.0) * 255.0));
                if (g < 85)
                    g = 42;
                if (g >= 85 && g < 170)
                    g = 127;
                if (g >= 170)
                    g = 212;
                c = Color.FromArgb(r, g, b);

            }
            if (d >= 25 && d < 30)
            {
                r = 255;
                g = 0;
                b = 0;
                b += ((int)(((d - 25.0) / 5.0) * 255.0));
                if (b < 85)
                    b = 42;
                if (b >= 85 && b < 170)
                    b = 127;
                if (b >= 170)
                    b = 212;
                c = Color.FromArgb(r, g, b);
            }
            if (d >= 30 && d < 34.5)
            {
                r = 255;
                g = 0;
                b = 255;
                g += ((int)(((d - 30.0) / 4.5) * 255.0));
                if (g < 85)
                    g = 42;
                if (g >= 85 && g < 170)
                    g = 127;
                if (g >= 170)
                    g = 212;
                c = Color.FromArgb(r, g, b);
            }
            if (d >= 34.5)
                c = Color.Transparent;
            //===================================================================

            return c;
        }

        private Color MakeColor26(double d)
        {
            Color c = new Color();

            int r, g, b = 0;

            if (d < 26)
                c = Color.Transparent;
            if (d >= 26 && d < 28)
            {
                r = 0;
                g = 255;
                b = 255;
                b -= ((int)(((d - 26.0) / 2.0) * 255.0));
                c = Color.FromArgb(r, g, b);
            }
            if (d >= 28 && d < 30)
            {
                r = 0;
                g = 255;
                b = 0;
                r += ((int)(((d - 28.0) / 2.0) * 255.0));
                c = Color.FromArgb(r, g, b);
            }
            if (d >= 30 && d < 32)
            {
                r = 255;
                g = 255;
                b = 0;
                //g += ((int)(((d - 28.0) / 6.5) * 255.0));
                g -= ((int)(((d - 30.0) / 2.0) * 255.0));
                c = Color.FromArgb(r, g, b);

            }
            if (d >= 32 && d < 34)
            {
                r = 255;
                g = 0;
                b = 0;
                b += ((int)(((d - 32.0) / 2.0) * 255.0));
                c = Color.FromArgb(r, g, b);
            }
            if (d >= 34 && d < 34.5)
            {
                r = 255;
                g = 0;
                b = 255;
                g += ((int)(((d - 34.0) / 0.5) * 255.0));
                c = Color.FromArgb(r, g, b);
            }
            if (d >= 34.5)
                c = Color.Transparent;


            return c;
        }

        private static Color MakeColorDelta(double d)
        {
            Color c = new Color();

            int r, g, b = 0;
            if (d < -2)
                c = Color.FromArgb(0, 0, 255);
            if (d >= -2 && d < -1)
            {
                r = 0;
                g = 0;
                b = 255;
                g += 255 - ((int)(((Math.Abs(d) - 1.0) / 1.0) * 255.0));
                c = Color.FromArgb(r, g, b);
            }
            if (d >= -1 && d < 0)
            {
                r = 0;
                g = 255;
                b = 255;
                b -= 255 - ((int)(((Math.Abs(d)) / 1.0) * 255.0));
                c = Color.FromArgb(r, g, b);
            }
            if (d >= 0 && d < 1)
            {
                r = 0;
                g = 255;
                b = 0;
                r += ((int)(((Math.Abs(d)) / 1.0) * 255.0));
                c = Color.FromArgb(r, g, b);
            }
            if (d >= 1 && d < 2)
            {
                r = 255;
                g = 255;
                b = 0;
                g -= ((int)(((Math.Abs(d) - 1.0) / 1.0) * 255.0));
                c = Color.FromArgb(r, g, b);
            }
            if (d >= 2 && d < 34.5)
                c = Color.FromArgb(255, 0, 0);

            if (d >= 34.5)
                c = Color.Transparent;


            return c;
        }
        #endregion

        #region Private Helpers
        private DateTime DecodeFileName(string fileName)
        {
            // Leap Years?...who knows
            string[] toks = fileName.Split('.');
            DateTime ret = new DateTime(int.Parse(toks[2]), 1, 1);
            ret = ret.AddDays(int.Parse(toks[3]) - 1);

            return ret;
        }
        #endregion
    }
}
